package com.rest.core.filter;

import java.io.IOException;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.MultivaluedMap;

import org.glassfish.jersey.message.internal.HeaderUtils;

/**
 * 请求日志记录
 * 
 * @Description ClientRequestFilter：客户端请求过滤器， ContainerRequestFilter：服务端请求过滤器，
 *              ContainerResponseFilter：服务端响应过滤器， ClientResponseFilter：客户端响应过滤器
 * @Package com.rest.core.filter.AirLogFilter
 * @author Smile-
 * @date 2016年4月10日 下午5:32:16
 */
@PreMatching
public class AirLogFilter implements ClientRequestFilter,
		ContainerRequestFilter, ContainerResponseFilter, ClientResponseFilter {
	private static final String NOTIFICATION_PREFIX = "* ";
	private static final String SERVER_REQUEST = "> ";
	private static final String SERVER_RESPONSE = "< ";
	private static final AtomicLong logSequence = new AtomicLong(0);

	/**
	 * 客户端请求过滤器
	 */
	public void filter(ClientRequestContext requestContext) throws IOException {
		System.out.println("客户端请求过滤器");
	}

	/**
	 * 服务端请求过滤器
	 */
	@Override
	public void filter(ContainerRequestContext requestContext)
			throws IOException {
		System.out.println("服务端请求过滤器");
		long id = logSequence.incrementAndGet();
		StringBuilder b = new StringBuilder();
		printRequestLine(SERVER_REQUEST, b, id, requestContext.getMethod(),
				requestContext.getUriInfo().getRequestUri());
		printPrefixedHeaders(SERVER_REQUEST, b, id, requestContext.getHeaders());
		System.out.println(b);
	}

	/**
	 * 服务端响应过滤器
	 */
	@Override
	public void filter(ContainerRequestContext requestContext,
			ContainerResponseContext responseContext) throws IOException {
		System.out.println("服务端响应过滤器");
		long id = logSequence.incrementAndGet();
		StringBuilder b = new StringBuilder();
		printResponseLine(SERVER_RESPONSE, b, id, responseContext.getStatus());
		printPrefixedHeaders(SERVER_RESPONSE, b, id,
				HeaderUtils.asStringHeaders(responseContext.getHeaders()));
		System.out.println(b);
	}

	/**
	 * 客户端响应过滤器
	 */
	@Override
	public void filter(ClientRequestContext requestContext,
			ClientResponseContext responseContext) throws IOException {
		System.out.println("客户端响应过滤器");
	}

	private void printPrefixedHeaders(final String prefix, StringBuilder b,
			long id, MultivaluedMap<String, String> headers) {
		for (Map.Entry<String, List<String>> e : headers.entrySet()) {
			List<?> val = e.getValue();
			String header = e.getKey();

			if (val.size() == 1) {
				prefixId(b, id).append(prefix).append(header).append(": ")
						.append(val.get(0)).append("\n");
			} else {
				StringBuilder sb = new StringBuilder();
				boolean add = false;
				for (Object s : val) {
					if (add) {
						sb.append(',');
					}
					add = true;
					sb.append(s);
				}
				prefixId(b, id).append(prefix).append(header).append(": ")
						.append(sb.toString()).append("\n");
			}
		}
	}

	private StringBuilder prefixId(StringBuilder b, long id) {
		b.append(Long.toString(id)).append(" ");
		return b;
	}

	private void printRequestLine(final String prefix, StringBuilder b,
			long id, String method, URI uri) {
		prefixId(b, id).append(NOTIFICATION_PREFIX)
				.append("AirLog - Request received on thread ")
				.append(Thread.currentThread().getName()).append("\n");
		prefixId(b, id).append(prefix).append(method).append(" ")
				.append(uri.toASCIIString()).append("\n");
	}

	private void printResponseLine(final String prefix, StringBuilder b,
			long id, int status) {
		prefixId(b, id).append(NOTIFICATION_PREFIX)
				.append("AirLog - Response received on thread ")
				.append(Thread.currentThread().getName()).append("\n");
		prefixId(b, id).append(prefix).append(Integer.toString(status))
				.append("\n");
	}
}
